package cc.owoo.godpen.file;

import cc.owoo.godpen.util.Path;

import java.io.File;
import java.util.Arrays;
import java.util.LinkedList;

/**
 * 文件夹构建器（有线程安全处理）
 * Created by nimensei
 * 2022-03-20 下午 07:02
 **/
public class FolderBuild {
    private final FolderInfo folder;// 文件夹
    private final int depth;// 深度
    private int size;// 文件夹数量

    /**
     * 构建目录结构
     *
     * @param root  根目录
     * @param depth 深度，下标为0的值可以传-1，代表无限个文件夹，其他均不能小于1
     */
    public FolderBuild(File root, int... depth) {
        if (depth.length < 1)
            throw new IllegalArgumentException("深度长度不能小于1");
        for (int i = 1; i < depth.length; i++)
            if (depth[i] <= 0)
                throw new IllegalArgumentException("深度值不能小于1");
        if (!root.isDirectory())
            if (!root.mkdirs())
                throw new IllegalArgumentException("创建文件夹失败");
        this.depth = depth.length;
        Structure structure = new Structure();
        structure.size = Math.max(-1, depth[0]);
        Structure now = structure;
        for (int i = 1; i < depth.length; i++) {
            now = now.child = new Structure();
            now.size = depth[i];
        }
        folder = new FolderInfo(root, structure);
        int[] count = {0};
        folder.build(count);
        folder.sort();
        size = count[0];
    }

    /**
     * 创建一个文件夹
     *
     * @return 文件夹相对路径
     */
    public FilePath create() {
        synchronized (this) {
            FilePath info = folder.create();
            if (info == null)
                return null;
            ++size;
            return info;
        }
    }

    /**
     * 删除文件夹
     *
     * @param relative 相对路径
     * @return 是否删除成功
     */
    public boolean delete(Path relative) {
        if (relative == null)
            throw new NullPointerException("路径不能为空");
        if (relative.size() != depth)
            throw new IllegalArgumentException("路径的长度应该和深度一致");
        LinkedList<String> list = relative.toList();
        synchronized (this) {
            if (folder.delete(list)) {
                --size;
                return true;
            }
        }
        return false;
    }

    /**
     * 获取当前文件夹数量
     *
     * @return 文件夹数量
     */
    public int size() {
        synchronized (this) {
            return size;
        }
    }

    /**
     * 结构信息
     */
    private static class Structure {
        public int size;// 数量
        public Structure child;// 子结构
    }

    /**
     * 文件夹信息
     */
    private static class FolderInfo {
        private final Folder folder;// 该层文件夹
        private final Structure structure;// 结构
        private LinkedList<FolderInfo> list;// 可用的子文件夹

        public FolderInfo(File root, Structure structure) {
            this.folder = new Folder(root);
            this.structure = structure;
        }

        /**
         * 计算子文件夹数量，并排序
         *
         * @return 子文件夹数量
         */
        public int sort() {
            if (structure.child == null)
                return folder.size();
            if (list == null || list.size() == 0)
                return 0;
            if (list.size() == 1)
                return list.getFirst().sort();
            int count = 0;
            FolderSort[] array = new FolderSort[list.size()];
            int index = 0;
            for (FolderInfo info : list) {
                int now = info.sort();
                count += now;
                array[index++] = new FolderSort(info, now);
            }
            Arrays.sort(array, (a, b) -> b.count - a.count);
            list.clear();
            for (FolderSort folderSort : array)
                list.add(folderSort.info);
            return count;
        }

        /**
         * 构建目录
         *
         * @return 是否有空位
         */
        public boolean build(int[] count) {
            if (structure.child == null) {
                count[0] += folder.size();
                return folder.size() < structure.size;
            }
            for (String s : folder) {
                FolderInfo info = new FolderInfo(new File(folder.getPath() + "/" + s), structure.child);
                if (!info.build(count))
                    continue;
                if (list == null)
                    list = new LinkedList<>();
                list.add(info);
            }
            return list != null;
        }

        /**
         * 创建一个文件夹
         *
         * @return 文件夹相对路径
         */
        public FilePath create() {
            if (list == null)
                list = new LinkedList<>();
            while (list.size() > 0) {
                FolderInfo info = list.getFirst();
                FilePath path = info.create();
                if (path == null) {
                    list.removeFirst();
                    continue;
                }
                path.relative.addFirstName(info.folder.getName());
                return path;
            }
            if (structure.size > 0 && folder.size() >= structure.size)
                return null;
            File file = folder.create();
            if (structure.child == null)
                return new FilePath(file, new Path(file.getName()));
            FolderInfo info = new FolderInfo(file, structure.child);
            list.add(info);
            FilePath path = info.create();
            if (path == null)
                throw new RuntimeException("创建文件夹为空，但逻辑上来说，这不应该");
            path.relative.addFirstName(file.getName());
            return path;
        }

        /**
         * 删除文件夹
         *
         * @param list 路径
         * @return 是否删除成功
         */
        public boolean delete(LinkedList<String> list) {
            String name = list.removeFirst();
            if (this.list != null)
                for (var info : this.list)
                    if (info.folder.getName().equals(name))
                        return info.delete(list);
            if (list.size() == 0)
                return folder.delete(name);
            if (!folder.contains(name))
                return false;
            StringBuilder path = new StringBuilder();
            path.append(folder.getPath());
            path.append('/').append(name);
            for (String s : list)
                path.append('/').append(s);
            return new File(path.toString()).delete();
        }
    }

    private record FolderSort(FolderInfo info, int count) {
    }
}
